/*
 * @Author: Lin Hongwei
 * @Date: 2022-10-02 10:15:07
 * @LastEditTime: 2022-10-02 22:42:15
 * @LastEditors: Lin Hongwei
 * @email: 1365755396@qq.com
 * @CSDN: https://blog.csdn.net/weixin_45429409
 * @Description: 后退前进时空穿梭函数
 * 以当前值为中心分为左右两个栈
 * - 左边栈（后退栈）：记录可以后退的值，右边为栈顶
 *       -- 后退：出栈，出的值为新的当前值，旧的值交给前进栈入栈
 * - 右边栈（前进栈）：记录可以前进的值，左边为栈顶
 *       -- 前进：出栈，出的值为新的当前值，旧的值交给后退栈入栈
 */

import { type } from "os";
import { useCallback, useReducer, useState } from "react";

export const useUndo1 = <T>(initState: T) => {
  const [current, setCurrent] = useState(initState); //现在的值
  const [forward, setForward] = useState<T[]>([]); //存储可以前进的记录
  const [back, setBack] = useState<T[]>([]); //存储可以后退的记录

  const canForward = !!forward.length; //是否可以前进
  const canBack = !!back.length; //是否可以后退

  /**
   * @description: 后退操作
   */
  const undo = () => {
    if (!canBack) return;

    const newVal = back[back.length - 1]; //新的值为后退栈出栈的值
    const newBack = back.slice(0, back.length - 1); //后退栈出栈
    const newForward = [current, ...forward]; //前进栈入栈

    setCurrent(newVal);
    setBack(newBack);
    setForward(newForward);
  };

  /**
   * @description: 前进
   */
  const redo = () => {
    if (!canForward) return;

    const newVal = forward[0]; //新的值为前进栈出栈的值
    const newBack = [...back, current]; //后退栈入栈
    const newForward = forward.slice(1); //前进栈出栈
    setCurrent(newVal);
    setBack(newBack);
    setForward(newForward);
  };

  /**
   * @description: 设置
   */
  const set = (newVal: T) => {
    if (newVal == current) return;
    setBack([...back, current]); //入栈旧值
    setCurrent(newVal); //赋值新值
    setForward([]); //已经是最新的值不需要前进
  };

  const reset = (newVal: T = initState) => {
    setCurrent(newVal);
    setBack([]);
    setForward([]);
  };

  return [
    {
      current,
      canForward,
      canBack,
    },
    {
      undo,
      redo,
      set,
      reset,
    },
  ] as const;
};

/**
 * @description: 合并state，且用useCallback优化
 */
export const useUndo2 = <T>(initState: T) => {
  const [state, setState] = useState<{
    current: T;
    forward: T[];
    back: T[];
  }>({
    current: initState,
    forward: [],
    back: [],
  });

  const canForward = !!state.forward.length; //是否可以前进
  const canBack = !!state.back.length; //是否可以后退

  /**
   * @description: 后退操作
   */
  const undo = useCallback(() => {
    setState((currentSate) => {
      if (!canBack) return currentSate;
      const { current, back, forward } = currentSate;
      const newVal = back[back.length - 1]; //新的值为后退栈出栈的值
      const newBack = back.slice(0, back.length - 1); //后退栈出栈
      const newForward = [current, ...forward]; //前进栈入栈
      return {
        back: newBack,
        current: newVal,
        forward: newForward,
      };
    });
  }, [canBack]);

  /**
   * @description: 前进
   */
  const redo = useCallback(() => {
    setState((currentSate) => {
      if (!canForward) return currentSate;
      const { current, back, forward } = currentSate;
      const newVal = forward[0]; //新的值为前进栈出栈的值
      const newBack = [...back, current]; //后退栈入栈
      const newForward = forward.slice(1); //前进栈出栈
      return {
        back: newBack,
        current: newVal,
        forward: newForward,
      };
    });
  }, [canForward]);

  /**
   * @description: 设置
   */
  const set = useCallback((newVal: T) => {
    setState((currentSate) => {
      const { current, back } = currentSate;
      if (newVal == current) return currentSate;
      return {
        back: [...back, current], //入栈旧值
        current: newVal, //赋值新值
        forward: [], //已经是最新的值不需要前进
      };
    });
  }, []);

  const reset = useCallback(
    (newVal: T = initState) => {
      setState(() => {
        return {
          back: [],
          current: newVal,
          forward: [],
        };
      });
    },
    [initState]
  );

  return [
    state,
    {
      undo,
      redo,
      set,
      reset,
      canBack,
      canForward,
    },
  ] as const;
};

/**
 * @description: 使用useReducer优化
 */

//定义一系列action的type
const UNDO = "UNDO";
const REDO = "REDO";
const SET = "SET";
const RESET = "RESET";

//抽离state状态
type StateType<T> = {
  current: T;
  forward: T[];
  back: T[];
};

type ActionType<T> = {
  type: typeof UNDO | typeof REDO | typeof SET | typeof RESET;
  newSetVal?: T;
};
//reducer函数
const undoReducer = <T>(state: StateType<T>, action: ActionType<T>) => {
  const { current, back, forward } = state;
  const { newSetVal } = action;
  switch (action.type) {
    case UNDO:
      if (!back.length) return state;
      return {
        current: back[back.length - 1], //新的值为后退栈出栈的值
        back: back.slice(0, back.length - 1), //后退栈出栈
        forward: [current, ...forward], //前进栈入栈
      };
    case REDO:
      if (!forward.length) return state;
      return {
        back: [...back, current], //后退栈入栈
        current: forward[0], //新的值为前进栈出栈的值
        forward: forward.slice(1), //前进栈出栈
      };
    case SET:
      if (newSetVal == current) return state;
      return {
        back: [...back, current], //入栈旧值
        current: newSetVal, //赋值新值
        forward: [], //已经是最新的值不需要前进
      };
    case RESET:
      return {
        back: [],
        current: newSetVal,
        forward: [],
      };
    default:
      return state;
  }
};

export const useUndo = <T>(initState: T) => {
  const [state, dispatch] = useReducer(undoReducer, {
    current: initState,
    forward: [],
    back: [],
  } as StateType<T>);

  const canForward = !!state.forward.length; //是否可以前进
  const canBack = !!state.back.length; //是否可以后退

  /**
   * @description: 后退操作
   */
  const undo = useCallback(() => dispatch({ type: UNDO }), []);

  /**
   * @description: 前进
   */
  const redo = useCallback(() => dispatch({ type: REDO }), []);

  /**
   * @description: 设置
   */
  const set = useCallback(
    (newSetVal: T) => dispatch({ type: SET, newSetVal }),
    []
  );

  const reset = useCallback(
    (newSetVal: T = initState) => dispatch({ type: RESET, newSetVal }),
    [initState]
  );

  return [
    state as StateType<T>,
    {
      undo,
      redo,
      set,
      reset,
      canBack,
      canForward,
    },
  ] as const;
};
